// DataReader.h

#pragma once

#include <fstream>
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <string.h>

#define DataReaderIniFile "Reader.ini"
#define MaxStrlen 1024

using namespace std;

class DataReader
{
public:
	enum Datatype
	{
		Int32,
		Double,
		String,
	};

	enum DataArrangement
	{
		TuplePerLine,
		SingleLine,
	};

	enum DataFormat
	{
		Normal,
		Missing,
		Sparse,
	};

public:
	string Filename;
	vector<Datatype> DatatypeList;
	char TerminationChar;
	DataArrangement Arrangement;
	DataFormat Format;
	int TupleNumber;
	int AttNumber;
	vector<int> KilledTupleList; // Kill the tuples which are counted in TupleNumber
	vector<int> KilledAttList;
	vector<int> KilledLineList; // Kill the lines which are not counted in TupleNumber
	
private:
	ifstream In;
	unsigned int KilledTuplePoint;
	unsigned int KilledAttPoint;
	unsigned int KilledLinePoint;
	int TupleCounter;
	int AttCounter;
	int LineCounter;

public:
	DataReader();
	DataReader(string IniFilename);
  DataReader(string IniFilename, string input_file_direction);
	~DataReader();

private:
	void Initial(string IniFilename, string input_file_direction);

public:
	bool Open(void);
	void Close(void);
	bool Fail(void);

	template<class T>
	int ReadValue(T& Value);
	bool SkipLine(void);

	int GetTupleNumber(void);
	int GetAttNumber(void);
};

// Return Value List
// 1: Normal ends
// 0: No data or no opened input file
// -1: Missing
template<class T>
int DataReader::ReadValue(T& Value)
{
	char TempCharArr[MaxStrlen + 1];

	if(Fail() ||
		TupleCounter >= TupleNumber ||
		AttCounter >= AttNumber)
	{
		return(0);
	}
	
	for(;;) // KillLine
	{
		if(KilledLinePoint < KilledLineList.size() && KilledLineList[KilledLinePoint] == LineCounter)
		{
			if(!SkipLine())
			{
				return(0);
			}
			KilledLinePoint++;
		}
		else
		{
			break;
		}
	}

	if(Arrangement == TuplePerLine)
	{
		if(KilledAttList.size() > 0) // KillAtt
		{
			for(; AttCounter == KilledAttList[KilledAttPoint];)
			{
				if(AttCounter == AttNumber - 1) // The last att value of each tuple
				{
					In.getline(TempCharArr, MaxStrlen);
					LineCounter++;
				}
				else
				{
					In.getline(TempCharArr, MaxStrlen, TerminationChar);
				}
				AttCounter++;
				if(AttCounter >= AttNumber)
				{
					AttCounter = 0;
					TupleCounter++;
				}

				KilledAttPoint++;
				if(KilledAttPoint >= KilledAttList.size())
				{
					KilledAttPoint = 0;
				}
			}
		}
		if(AttCounter == AttNumber - 1) // The last att value of each tuple
		{
			In.getline(TempCharArr, MaxStrlen);
			LineCounter++;
		}
		else
		{
			In.getline(TempCharArr, MaxStrlen, TerminationChar);
		}
		AttCounter++;
		if(AttCounter >= AttNumber)
		{
			AttCounter = 0;
			TupleCounter++;
		}
	}
	else if(Arrangement == SingleLine)
	{
		if((AttCounter == AttNumber - 1) && (TupleCounter == TupleNumber - 1)) // The last value
		{
			In.getline(TempCharArr, MaxStrlen);
			LineCounter++;
		}
		else
		{
			In.getline(TempCharArr, MaxStrlen, TerminationChar);
		}
		AttCounter++;
		if(AttCounter >= AttNumber)
		{
			AttCounter = 0;
			TupleCounter++;
		}
	}

	if(In.eof())
	{
		return(0);
	}

	if(TempCharArr[0] == '\0') // Missing value detection
	{
		if((Format == Missing) || (Format == Sparse))
		{
			return(-1); // Missing
		}
		else
		{
			return(0);		
		}
	}
	else
	{
		stringstream TempSS(TempCharArr);
		TempSS >> Value;
	}

	return(1); // Finished
}
